home *** CD-ROM | disk | FTP | other *** search
- /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
- *
- * 16550 support plus some statistics added mah@hpuviea.at 15/7/89
- */
- #include <stdio.h>
- #include <dos.h>
- #include "global.h"
- #include "iface.h"
- #include "asy.h"
- #include "pc.h"
- #include "slip.h"
- #include "proc.h"
- #include "8250.h"
-
- static int asyrxint __ARGS((unsigned dev));
- static void asytxint __ARGS((unsigned dev));
-
- static struct asy Asy[ASY_MAX];
- unsigned Nasy;
- /* ASY interrupt handlers */
- static INTERRUPT (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
-
- /* Initialize asynch port "dev" */
- int
- asy_init(dev,iface,arg1,arg2,bufsize)
- int16 dev;
- struct iface *iface;
- char *arg1,*arg2; /* Attach args for address and vector */
- unsigned bufsize;
- {
- register unsigned base;
- register struct fifo *fp;
- register struct asy *ap;
- char i_state;
-
- ap = &Asy[dev];
- ap->iface = iface;
- ap->addr = htoi(arg1);
- ap->vec = htoi(arg2);
- /* Set up receiver FIFO */
- fp = &ap->fifo;
- if((fp->buf = malloc(bufsize)) == NULLCHAR){
- printf("asy%d: No space for rx buffer\r\n",dev);
- return -1;
- }
- fp->bufsize = bufsize;
- fp->wp = fp->rp = fp->buf;
- fp->cnt = 0;
- base = ap->addr;
-
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- i_state = dirps();
-
- /* Save original interrupt vector, mask state, control bits */
- ap->save.vec = getirq(ap->vec);
- ap->save.mask = getmask(ap->vec);
- ap->save.lcr = inportb(base+LCR);
- ap->save.ier = inportb(base+IER);
- ap->save.mcr = inportb(base+MCR);
-
- /* save speed bytes */
- setbit(base+LCR,LCR_DLAB);
- ap->save.divl = inportb(base+DLL);
- ap->save.divh = inportb(base+DLM);
- clrbit(base+LCR,LCR_DLAB);
-
- /* Set interrupt vector to SIO handler */
- setirq(ap->vec,Handle[dev]);
-
- /* Set line control register: 8 bits, no parity */
- outportb(base+LCR,(char)LCR_8BITS);
-
- /* determine if 16550, turn on FIFO mode and clear RX and TX FIFOs */
- outportb(base+FCR,(char) FIFO_ENABLE);
- if (inportb(base+IIR) & IIR_FIFO_ENABLED) {
- ap->is_16550 = 1;
- outportb(base+FCR,(char) FIFO_SETUP);
- } else
- ap->is_16550 = 0;
-
- /* Turn on receive interrupt enable in 8250, leave transmit
- * and modem status interrupts turned off for now
- */
- outportb(base+IER,(char)IER_DAV);
-
- /* Set modem control register: assert DTR, RTS, turn on 8250
- * master interrupt enable (connected to OUT2)
- */
- outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
-
- /* Enable interrupt */
- maskon(ap->vec);
- restore(i_state);
- return 0;
- }
- int
- asy_stop(iface)
- struct iface *iface;
- {
- register unsigned base;
- register struct asy *ap;
- char i_state;
-
- ap = &Asy[iface->dev];
- base = ap->addr;
-
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- /* and hardware fifos if available */
- if (ap->is_16550)
- outportb(base+FCR,(char) FIFO_SETUP);
-
- /* Restore original interrupt vector and 8259 mask state */
- i_state = dirps();
- setirq(ap->vec,ap->save.vec);
- if(ap->save.mask)
- maskon(ap->vec);
- else
- maskoff(ap->vec);
-
- /* Restore speed regs */
- setbit(base+LCR,LCR_DLAB);
- outportb(base+DLL,ap->save.divl); /* Low byte */
- outportb(base+DLM,ap->save.divh); /* Hi byte */
- clrbit(base+LCR,LCR_DLAB);
-
- /* Restore control regs */
- outportb(base+LCR,ap->save.lcr);
- outportb(base+IER,ap->save.ier);
- outportb(base+MCR,ap->save.mcr);
- restore(i_state);
- return 0;
- }
- /* Asynchronous line I/O control */
- int
- asy_ioctl(iface,argc,argv)
- struct iface *iface;
- int argc;
- char *argv[];
- {
- if(argc < 1){
- printf("%d\r\n",Asy[iface->dev].speed);
- return 0;
- }
- return asy_speed(iface->dev,atoi(argv[0]));
- }
- /* Set asynch line speed */
- int
- asy_speed(dev,speed)
- int16 dev;
- int speed;
- {
- register unsigned base;
- register int divisor;
- char i_state;
-
- if(speed == 0 || dev >= Nasy)
- return -1;
-
- base = Asy[dev].addr;
- Asy[dev].speed = speed;
-
- divisor = BAUDCLK / (long)speed;
-
- i_state = dirps();
-
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
- if (Asy[dev].is_16550) /* clear tx+rx fifos */
- outportb(base+FCR,(char) FIFO_SETUP);
-
- /* Turn on divisor latch access bit */
- setbit(base+LCR,LCR_DLAB);
-
- /* Load the two bytes of the register */
- outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
- outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
-
- /* Turn off divisor latch access bit */
- clrbit(base+LCR,LCR_DLAB);
-
- restore(i_state);
- return 0;
- }
-
- /* Send a buffer to serial transmitter */
- void
- asy_output(dev,buf,cnt)
- unsigned dev;
- char *buf;
- unsigned short cnt;
- {
- register struct dma *dp;
- unsigned base;
- char i_state;
-
- if(dev >= Nasy)
- return;
- base = Asy[dev].addr;
- dp = &Asy[dev].dma;
- i_state = dirps();
- if(dp->flags){
- restore(i_state);
- return; /* Already busy */
- }
- dp->data = buf;
- dp->cnt = cnt;
- dp->flags = 1;
-
- /* Enable transmitter buffer empty interrupt and simulate
- * an interrupt; this will get things rolling.
- */
- setbit(base+IER,IER_TxE);
- asytxint(dev);
- restore(i_state);
- }
- /* Blocking read from asynch line
- * Returns count of characters read
- */
- char
- get_asy(dev)
- int16 dev;
- {
- char i_state;
- register struct fifo *fp;
- char c;
-
- fp = &Asy[dev].fifo;
-
- i_state = dirps();
- while(fp->cnt == 0)
- pwait(fp);
- fp->cnt--;
- restore(i_state);
-
- c = *fp->rp++;
- if(fp->rp >= &fp->buf[fp->bufsize])
- fp->rp = fp->buf;
-
- return c;
- }
- /* Interrupt handler for 8250 asynch chip */
- void
- asyint(dev)
- unsigned dev;
- {
- register unsigned base;
- register char iir;
- struct fifo *fp;
- int cnt = 0;
-
- base = Asy[dev].addr;
- fp = &Asy[dev].fifo;
- while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
- switch(iir & IIR_ID){
- case IIR_RDA: /* Receiver interrupt */
- cnt += asyrxint(dev);
- break;
- case IIR_THRE: /* Transmit interrupt */
- asytxint(dev);
- break;
- }
- /* should happen at end of a single slip packet */
- if (iir & IIR_FIFO_TIMEOUT)
- Asy[dev].fifotimeouts++;
- }
- if(cnt != 0)
- psignal(fp,1);
- }
-
- /* Process 8250 receiver interrupts */
- static int
- asyrxint(dev)
- unsigned dev;
- {
- unsigned base;
- register struct fifo *fp;
- struct asy *asyp;
- char c;
- int cnt = 0;
- char lsr;
-
- asyp = &Asy[dev];
- base = asyp->addr;
- fp = &asyp->fifo;
- for(;;){
- lsr = inportb(base+LSR);
- if(lsr & LSR_OE)
- asyp->overrun++;
-
- if(lsr & LSR_DR){
- asyp->rxchar++;
- c = inportb(base+RBR);
- /* If buffer is full, we have no choice but
- * to drop the character
- */
- if(fp->cnt != fp->bufsize){
- *fp->wp++ = c;
- if(fp->wp >= &fp->buf[fp->bufsize])
- /* Wrap around */
- fp->wp = fp->buf;
- fp->cnt++;
- cnt++;
- } else
- asyp->rxdropped++;
- } else
- break;
- }
- asyp->rxints++;
- return cnt;
- }
- /* Handle 8250 transmitter interrupts */
- static void
- asytxint(dev)
- unsigned dev;
- {
- register struct dma *dp;
- register unsigned base;
- register int count;
- struct asy *asyp;
-
- asyp = &Asy[dev];
- base = asyp->addr;
- dp = &asyp->dma;
- if(!dp->flags){
- /* "Shouldn't happen", but disable transmit
- * interrupts anyway
- */
- clrbit(base+IER,IER_TxE);
- return; /* Nothing to send */
- }
-
- /* if it's a 16550, load up to 16 chars into the tx hw fifo
- * at once. With an 8250, it can be on char at most.
- */
-
- if (asyp->is_16550) {
- count = min(dp->cnt,OUTPUT_FIFO_SIZE);
-
- /* 16550: LSR_THRE will drop after the first char loaded
- * so we can't look at this bit to determine if the hw fifo is
- * full. There seems to be no way to determine if the tx fifo
- * is full (any clues?). So we should never get here while the
- * fifo isn't empty yet.
- */
- asyp->txchar += count;
- dp->cnt -= count;
-
- while(count--)
- outportb(base+THR,*dp->data++);
-
- if(dp->cnt == 0){
- dp->flags = 0;
- /* Disable transmit interrupts */
- clrbit(base+IER,IER_TxE);
- asytxdone(dev);
- }
- } else { /* 8250 */
- while(inportb(base+LSR) & LSR_THRE){
- asyp->txchar++;
- outportb(base+THR,*dp->data++);
-
- if(--dp->cnt == 0){
- dp->flags = 0;
- /* Disable transmit interrupts */
- clrbit(base+IER,IER_TxE);
- asytxdone(dev);
- break;
- }
- }
- }
- }
- int
- stxrdy(dev)
- int16 dev;
- {
- return(!Asy[dev].dma.flags);
- }
- int
- doasystat(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- register struct asy *asyp;
-
- for(asyp = Asy;asyp < &Asy[Nasy];asyp++){
- printf("%s: %s",asyp->iface->name,
- asyp->is_16550 ? "NS16550A" : "");
- printf(" RX: int %lu chr %lu ovrn %lu",
- asyp->rxints,asyp->rxchar,asyp->overrun);
- if(asyp->is_16550)
- printf(" fifotim %lu",asyp->fifotimeouts);
- printf(" TX: int %lu chr %lu\n",asyp->txints,asyp->txchar);
- }
- return 0;
- }
-
-